package com.tiancheng.trade.authserver.utils;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.util.encoders.UrlBase64;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @Author: kellen
 * @Description:
 * @Date: create in 2024/11/13 11:14
 */
public class RSAUtils {
    public RSAUtils() {
    }

    public static StringKeyPair genStringKeyPair(int keySize) {
        try {
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
            keyPairGen.initialize(keySize, new SecureRandom());
            KeyPair keyPair = keyPairGen.generateKeyPair();
            RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();
            RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
            String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
            String privateKeyString = new String(Base64.encodeBase64(privateKey.getEncoded()));
            return new StringKeyPair(privateKeyString, publicKeyString);
        } catch (Exception var7) {
            throw new RuntimeException(var7);
        }
    }

    public static KeyPair generateKeyPair(String publicKey, String privateKey) {
        try {
            PublicKey rsaPublicKey = null;
            if (privateKey != null) {
                byte[] publicKeyDecoded = Base64.decodeBase64(publicKey);
                rsaPublicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyDecoded));
            }

            PrivateKey rsaPrivateKey = null;
            if (privateKey != null) {
                byte[] privateKeyDecoded = Base64.decodeBase64(privateKey);
                rsaPrivateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyDecoded));
            }

            return new KeyPair(rsaPublicKey, rsaPrivateKey);
        } catch (Exception var5) {
            throw new RuntimeException(var5);
        }
    }

    private static String encrypt(String str, String key, boolean isPublicKey) {
        try {
            byte[] decoded = Base64.decodeBase64(key);
            Object encryptKey;
            if (isPublicKey) {
                encryptKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
            } else {
                encryptKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
            }

            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(1, (Key)encryptKey);
            byte[] bin = str.getBytes(StandardCharsets.UTF_8);
            int blockSize = cipher.getBlockSize();
            if (blockSize <= 0) {
                return new String(UrlBase64.encode(cipher.doFinal(bin)));
            } else {
                int outputSize = cipher.getOutputSize(bin.length);
                int leavedSize = bin.length % blockSize;
                int blocksSize = leavedSize != 0 ? bin.length / blockSize + 1 : bin.length / blockSize;
                byte[] raw = new byte[outputSize * blocksSize];

                int remainSize;
                for(int i = 0; (remainSize = bin.length - i * blockSize) > 0; ++i) {
                    int inputLen = Math.min(remainSize, blockSize);
                    cipher.doFinal(bin, i * blockSize, inputLen, raw, i * outputSize);
                }

                return new String(UrlBase64.encode(raw));
            }
        } catch (Exception var15) {
            throw new RuntimeException(var15);
        }
    }

    public static String encryptByPublicKey(String str, String publicKey) {
        return encrypt(str, publicKey, true);
    }

    public static String encryptByPrivateKey(String str, String privateKey) {
        return encrypt(str, privateKey, false);
    }

    public static String decryptByPrivateKey(String str, String privateKey) throws Exception {
        byte[] inputByte = UrlBase64.decode(str.getBytes(StandardCharsets.UTF_8));
        byte[] decoded = Base64.decodeBase64(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey)KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(2, priKey);
        int blockSize = cipher.getBlockSize();
        if (blockSize <= 0) {
            return new String(cipher.doFinal(inputByte));
        } else {
            ByteArrayOutputStream bout = new ByteArrayOutputStream(64);

            for(int j = 0; inputByte.length - j * blockSize > 0; ++j) {
                bout.write(cipher.doFinal(inputByte, j * blockSize, blockSize));
            }

            return bout.toString();
        }
    }

    public static String decryptByPublicKey(String str, String publicKey) {
        try {
            byte[] inputByte = UrlBase64.decode(str.getBytes(StandardCharsets.UTF_8));
            byte[] decoded = Base64.decodeBase64(publicKey);
            RSAPublicKey pubKey = (RSAPublicKey)KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(2, pubKey);
            return new String(cipher.doFinal(inputByte));
        } catch (Exception var6) {
            throw new RuntimeException(var6);
        }
    }

    public static class StringKeyPair {
        private String privateKey;
        private String publicKey;

        public String getPrivateKey() {
            return this.privateKey;
        }

        public String getPublicKey() {
            return this.publicKey;
        }

        public StringKeyPair(String privateKey, String publicKey) {
            this.privateKey = privateKey;
            this.publicKey = publicKey;
        }
    }
}
